package io.itit.ecp.admin.provide.action.controller;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import icu.mhb.mybatisplus.plugln.core.JoinLambdaWrapper;
import icu.mhb.mybatisplus.plugln.extend.Joins;
import io.itit.ecp.admin.common.enums.IActivityCarbonEmissionTypeEnums;
import io.itit.ecp.admin.provide.action.req.DatFillingQueryReq;
import io.itit.ecp.admin.provide.action.req.DataFillingReq;
import io.itit.ecp.admin.provide.action.resp.DataFillingDataResp;
import io.itit.ecp.admin.provide.action.resp.DataFillingEnergyConsumptionTypeResp;
import io.itit.ecp.admin.provide.action.resp.DataFillingResp;
import io.itit.ecp.admin.provide.helper.SceneHelper;
import io.itit.ecp.admin.server.entity.*;
import io.itit.ecp.admin.server.service.*;
import io.itit.grass.common.provide.action.req.BaseIdReq;
import io.itit.grass.common.provide.action.resp.ResultResp;
import io.itit.grass.common.provide.action.validation.*;
import io.itit.grass.common.server.utils.ConvertUtils;
import io.itit.grass.estelle.starter.core.ExcelXSSFUtil;
import io.itit.grass.estelle.starter.util.JsonUtil;
import io.itit.grass.web.wall.utils.PageUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据填报控制类
 */
@Api(tags = "数据填报")
@RestController
@RequestMapping("/ecp-admin/dataFilling")
public class DataFillingController {

    @Resource
    private IDataFillingService dataFillingService;

    @Resource
    private IDataFillingDetailService dataFillingDetailService;

    @Resource
    private IDataFillingDataService dataFillingDataService;

    @Resource
    private IEmissionReductionFactorService emissionReductionFactorService;

    @Resource
    private ICarbonEmissionFactorService carbonEmissionFactorService;

    @Resource
    private IEnergyConsumptionTypeService energyConsumptionTypeService;

    @Resource
    private IActivityCarbonEmissionService activityCarbonEmissionService;

    @Resource
    private ISceneRoomService sceneRoomService;

    @Resource
    private ISceneFloorService sceneFloorService;

    @Resource
    private ISceneEdificeService sceneEdificeService;

    @ApiOperation("分页查询")
    @PostMapping("queryPage")
    @RequiresPermissions("ecp-admin:dataFilling:search")
    public ResultResp<IPage<DataFillingDataResp>> queryPage(@RequestBody @Validated(IQueryPage.class) DatFillingQueryReq req) {
        Page<DataFillingDataResp> page = buildQW(req).joinPage(PageUtils.getPage(req, DataFillingDataResp.class), DataFillingDataResp.class);
        return ResultResp.success(SceneHelper.i().setSceneInfo(page));
    }

    @ApiOperation("查询详情")
    @GetMapping("detail")
    @RequiresPermissions("ecp-admin:dataFilling:search")
    public ResultResp<DataFillingResp> queryPage(@RequestParam String id) {
        DataFillingEntity fillingEntity = dataFillingService.getById(id);
        if (Objects.isNull(fillingEntity)) {
            return ResultResp.fail("查询数据不存在");
        }
        DataFillingResp fillingResp = ConvertUtils.convert(fillingEntity, DataFillingResp.class);
        ActivityCarbonEmissionEntity emissionEntity = activityCarbonEmissionService.getById(fillingEntity.getActivityCarbonEmissionId());
        if (Objects.nonNull(emissionEntity)) {
            fillingResp.setActivityType(emissionEntity.getActivityType());
            fillingResp.setActivityName(emissionEntity.getActivityName());
        }
        List<DataFillingDetailEntity> detailEntities = dataFillingDetailService.findByDataFillingId(id);
        fillingResp.setDetailJson(JSON.toJSONString(detailEntities));
        BigDecimal area = BigDecimal.ZERO;
        if (StringUtils.hasLength(fillingEntity.getSceneRoomId())) {
            area = sceneRoomService.getById(fillingEntity.getSceneRoomId()).getRoomArea();
        } else if (StringUtils.hasLength(fillingEntity.getSceneFloorId())) {
            area = sceneFloorService.getById(fillingEntity.getSceneFloorId()).getFloorArea();
        } else if (StringUtils.hasLength(fillingEntity.getSceneEdificeId())) {
            area = sceneEdificeService.getById(fillingEntity.getSceneEdificeId()).getFloorArea();
        }
        fillingResp.setArea(ObjectUtil.defaultIfNull(area, BigDecimal.ZERO));
        fillingResp.setTotalCarbonEmissionsValue(dataFillingDataService.totalCarbonEmissionByDataFillingId(id));
        List<DataFillingResp> dataFillingResps = SceneHelper.i().setSceneInfo(Collections.singletonList(fillingResp));

        return ResultResp.success(dataFillingResps.get(0));
    }

    @ApiOperation("新增")
    @PostMapping("add")
    @RequiresPermissions("ecp-admin:dataFilling:add")
    public ResultResp<Boolean> add(@RequestBody @Validated({IAdd.class}) DataFillingReq req) {
        return ResultResp.success(dataFillingService.saveOrUpdateDetail(ConvertUtils.convertIgnoreId(req, DataFillingEntity.class), req.getDetailJson()));
    }

    @ApiOperation("编辑")
    @PostMapping("update")
    @RequiresPermissions("ecp-admin:dataFilling:update")
    public ResultResp<Boolean> update(@RequestBody @Validated({IUpdate.class}) DataFillingReq req) {
        return ResultResp.success(dataFillingService.saveOrUpdateDetail(ConvertUtils.copy(req, DataFillingEntity.class), req.getDetailJson()));
    }

    @ApiOperation("删除(根据Id删)")
    @PostMapping("deleteById")
    @RequiresPermissions("ecp-admin:dataFilling:deleteById")
    public ResultResp<Boolean> deleteById(@RequestBody @Validated({IRequired.class}) BaseIdReq req) {
        return ResultResp.success(dataFillingService.removeByDataFillingDataId(req.getId()));
    }

    /**
     * 碳排放=（用量/天数*对应月份的碳排放因子）*对应月份的天数再相加
     * 例如:数据时间为11.15-12.15，用量为300,（11月份的碳排放因子为2.66，12月份的碳排放因子为2.99）
     * <p>
     * 计算：
     * 碳排放=（300/30*2.66）*15+（300/30*2.99）*15=碳排放量
     */
    @ApiOperation("计算碳排放")
    @GetMapping("calculateCarbonEmission")
    public ResultResp<BigDecimal> calculateCarbonEmission(
            @RequestParam String energyConsumptionTypeId,
            @RequestParam BigDecimal usage,
            @RequestParam String startTime,
            @RequestParam String endTime) {
        LocalDate start = LocalDate.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        LocalDate end = LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        List<CarbonEmissionFactorEntity> factorEntities = carbonEmissionFactorService
                .list(new LambdaQueryWrapper<CarbonEmissionFactorEntity>()
                        .eq(CarbonEmissionFactorEntity::getEnergyTypeId, energyConsumptionTypeId)
                        .ge(CarbonEmissionFactorEntity::getSolidifyDate, start)
                        .le(CarbonEmissionFactorEntity::getSolidifyDate, end));
        BigDecimal result = BigDecimal.ZERO;
        for (CarbonEmissionFactorEntity item : factorEntities) {
            LocalDate localDate = item.getSolidifyDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            int dayOfMonth = localDate.getDayOfMonth();
            int lengthOfMonth = localDate.lengthOfMonth();
            result = result.add(usage.divide(new BigDecimal(lengthOfMonth), RoundingMode.HALF_DOWN).multiply(item.getFactor())
                    .multiply(new BigDecimal(lengthOfMonth - dayOfMonth)));
        }
        return ResultResp.success(result);
    }

    /**
     * 活根据选择的活动类型，查询不同的能耗类型数据
     */
    @ApiOperation("查询能源种类")
    @GetMapping("energyConsumptionType")
    public ResultResp<List<DataFillingEnergyConsumptionTypeResp>> getEnergyConsumptionTypeList(@RequestParam String activityType) {
        Set<String> typeIds = new HashSet<>();
        Map<String, Double> typeIdTotalFactorMap = new HashMap<>();
        if (StringUtils.pathEquals(activityType, IActivityCarbonEmissionTypeEnums.CARBON_EMISSION.getValue())) {
            List<CarbonEmissionFactorEntity> factorEntities = carbonEmissionFactorService.list();
            typeIdTotalFactorMap = factorEntities.stream().collect(Collectors.groupingBy(CarbonEmissionFactorEntity::getEnergyTypeId, Collectors.summingDouble(item -> item.getFactor().doubleValue())));
            typeIds = factorEntities.stream().map(CarbonEmissionFactorEntity::getEnergyTypeId).collect(Collectors.toSet());
        } else if (StringUtils.pathEquals(activityType, IActivityCarbonEmissionTypeEnums.CARBON_OFFSETTING.getValue())) {
            List<EmissionReductionFactorEntity> factorEntities = emissionReductionFactorService.list();
            typeIds = factorEntities.stream().map(EmissionReductionFactorEntity::getEnergyTypeId).collect(Collectors.toSet());
            typeIdTotalFactorMap = factorEntities.stream().collect(Collectors.groupingBy(EmissionReductionFactorEntity::getEnergyTypeId,
                    Collectors.summingDouble(item -> item.getFactor().doubleValue())));
        }
        if (typeIds.isEmpty()) {
            return ResultResp.success(Collections.emptyList());
        }
        Map<String, Double> finalTypeIdTotalFactorMap = typeIdTotalFactorMap;
        List<DataFillingEnergyConsumptionTypeResp> list = energyConsumptionTypeService.list(new LambdaQueryWrapper<EnergyConsumptionTypeEntity>()
                        .in(EnergyConsumptionTypeEntity::getId, typeIds)
                        .eq(EnergyConsumptionTypeEntity::getStatus, "1")
                        .orderByAsc(EnergyConsumptionTypeEntity::getCreateTm)
                )
                .stream()
                .map(item -> {
                    DataFillingEnergyConsumptionTypeResp typeResp = new DataFillingEnergyConsumptionTypeResp();
                    typeResp.setId(item.getId());
                    typeResp.setEnergyName(item.getEnergyName());
                    typeResp.setEnergyUnit(item.getEnergyUnit());
                    Double value = finalTypeIdTotalFactorMap.get(item.getId());
                    typeResp.setTotalFactor(Objects.isNull(value) ? BigDecimal.ZERO : new BigDecimal(String.valueOf(value)));// TODO: 未确认
                    return typeResp;
                }).collect(Collectors.toList());
        return ResultResp.success(list);
    }


    private static JoinLambdaWrapper<DataFillingDataEntity> buildQW(DatFillingQueryReq req) {
        return Joins.of(DataFillingDataEntity.class)
                .leftJoin(ActivityCarbonEmissionEntity.class, ActivityCarbonEmissionEntity::getId, DataFillingDataEntity::getActivityCarbonEmissionId)
                .selectAs(ActivityCarbonEmissionEntity::getActivityType, DataFillingDataResp::getActivityType)
                .selectAs(ActivityCarbonEmissionEntity::getActivityName, DataFillingDataResp::getActivityName)
                .end()
                .leftJoin(SceneProjectEntity.class, SceneProjectEntity::getId, DataFillingDataEntity::getSceneProjectId)
                .selectAs(SceneProjectEntity::getProjectName, DataFillingDataResp::getProjectName)
                .end()
                .eq(StringUtils.hasLength(req.getActivityCarbonEmissionId()), DataFillingDataEntity::getActivityCarbonEmissionId, req.getActivityCarbonEmissionId())
                .eq(StringUtils.hasLength(req.getSceneProjectId()), DataFillingDataEntity::getSceneProjectId, req.getSceneProjectId())
                .eq(StringUtils.hasLength(req.getSceneEdificeId()), DataFillingDataEntity::getSceneEdificeId, req.getSceneEdificeId())
                .eq(StringUtils.hasLength(req.getSceneFloorId()), DataFillingDataEntity::getSceneFloorId, req.getSceneFloorId())
                .eq(StringUtils.hasLength(req.getSceneRoomId()), DataFillingDataEntity::getSceneRoomId, req.getSceneRoomId())
                .ge(Objects.nonNull(req.getDataStartTime()), DataFillingDataEntity::getSolidifyDate, req.getDataStartTime())
                .le(Objects.nonNull(req.getDataEndTime()), DataFillingDataEntity::getSolidifyDate, req.getDataEndTime())
                .orderByDesc(DataFillingDataEntity::getSolidifyDate);
    }

    @ApiOperation("导出")
    @PostMapping("export")
    @RequiresPermissions("ecp-admin:dataFilling:export")
    public void export(@RequestBody @Validated(IExport.class) DatFillingQueryReq req, HttpServletResponse response) {
        List<DataFillingDataResp> respList = SceneHelper.i().setSceneInfo(buildQW(req).joinList(DataFillingDataResp.class));
        ExcelXSSFUtil.export("DataFillingDataExport", JsonUtil.toJson(respList), response);
    }
}
